﻿using System;
using System.Text;
using System.Xml;
using System.Net;
using System.Collections.Specialized;
using System.Text.RegularExpressions;
using System.Net.Mail;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebPartPages;
using Microsoft.SharePoint.Administration;
using System.DirectoryServices;
using Microsoft.SharePoint.Portal.WebControls;
using NntpClientLib;
using NewsgroupListSync.WebPartReference;

namespace NewsgroupListSync
{
    class NewsgroupSyncJob : SPJobDefinition
    {
        [Persisted]
        private string _webURL;

        private string _newsserver;
        private string _newsgroup;
        private int _lastArticleSynced;
        private string _description;

        public string webURL
        {
            get { return _webURL; }
            set { _webURL = value; }
        }
        public string newsgroup
        {
            get { return _newsgroup; }
            set { _newsgroup = value; }
        }
        public string newsserver
        {
            get { return _newsserver; }
            set { _newsserver = value; }
        }
        public int lastArticleSynced
        {
            get { return _lastArticleSynced; }
            set { _lastArticleSynced = value; }
        }
        public string NewsGroupDescription
        {
            get { return _description; }
            set { _description = value; }
        }

        public NewsgroupSyncJob()
            : base()
        {
        }

        public NewsgroupSyncJob(string jobName, SPService service, SPServer server, SPJobLockType targetType)
            : base(jobName, service, server, targetType)
        {
        }

        public NewsgroupSyncJob(string jobName, SPWebApplication webApplication)
            : base(jobName, webApplication, null, SPJobLockType.Job)
        {
        }

        public NewsgroupSyncJob(string jobName, SPWebApplication webApplication, string URL)
            : base(jobName, webApplication, null, SPJobLockType.Job)
        {
            webURL = URL;
        }

        public override void Execute(Guid contentDbId)
        {
            using (SPSite site = new SPSite(webURL))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    if (!listExists(web.Lists, "Newsgroups"))
                    {
                        throw new SPException("Looks like there is no newsgroup list to sync from");
                    }
                    else
                    {
                        SPListItemCollection listItems = web.Lists["Newsgroups"].Items;
                        foreach (SPListItem item in listItems)
                        {
                            try
                            {
                                newsgroup = item["Newsgroup"].ToString();
                                newsserver = item["Title"].ToString();
                                lastArticleSynced = Convert.ToInt32(item["Last Article Synced"].ToString());
                                FetchArticles(item, web);
                            }
                            catch { }
                        }
                    }
                }
            }
        }

        private void FetchArticles(SPItem item, SPWeb web)
        {
            try
            {
                using (var client = new Rfc977NntpClientWithExtensions())
                {
                    client.Connect(newsserver);
                    client.SendModeReader();
                    client.SelectNewsgroup(newsgroup);

                    try
                    {
                        NewsGroupDescription = item["Group Description"].ToString();
                    }
                    catch
                    {
                        NewsGroupDescription = "";
                    }

                    if (!listExists(web.Lists, newsgroup))
                    {
                        createNewsgroupList(web);
                    }

                    Regex urlregex = new Regex(@"(http:\/\/([\w.]+\/?)\S*)", RegexOptions.IgnoreCase | RegexOptions.Compiled);
                    Regex emailregex = new Regex(@"([a-zA-Z_0-9.-]+\@[a-zA-Z_0-9.-]+\.\w+)", RegexOptions.IgnoreCase | RegexOptions.Compiled);

                    for (int postID = lastArticleSynced + 1; postID <= client.CurrentGroup.LastArticleId; postID++)
                    {
                        try
                        {
                            ArticleHeadersDictionary header = client.RetrieveArticleHeader(postID);

                            if (header != null)
                            {
                                IFormatProvider format = new System.Globalization.CultureInfo("en-US", true);
                                string s = header["Date"][0].Replace("(UTC)", "");
                                DateTime sentOn;
                                try
                                {
                                    sentOn = DateTime.Parse(s, format);
                                }
                                catch
                                {
                                    sentOn = DateTime.Now;
                                }
                                DateTime twoYears = DateTime.Now.AddYears(-2);

                                if (sentOn >= twoYears)
                                {
                                    header.AddHeader("PostID", postID.ToString());
                                    StringBuilder sb = new StringBuilder();
                                    foreach (string bodyLine in client.RetrieveArticleBody(postID))
                                    {
                                        string bl1 = urlregex.Replace(bodyLine, "<a href=\"$1\" target=\"_blank\">$1</a>");
                                        string bl2 = emailregex.Replace(bl1, "<a href=mailto:$1>$1</a>");
                                        sb.Append(bl2);
                                        sb.Append(Environment.NewLine);
                                    }
                                    sb.Replace("=20", "<br />");
                                    sb.Replace("\r\n", "<br />");
                                    sb.Replace("\n", "<br />");
                                    addNewsItem(header, sb, web);
                                    item["Last Article Synced"] = postID;
                                    item.Update();
                                }
                            }
                        }
                        catch (NntpResponseException error)
                        {
                            if (error.LastResponseCode == 423)
                            {
                                item["Last Article Synced"] = postID;
                                item.Update();
                                continue;
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw new SPException("I had an issue (" + ex.Message + ") running fetcharticles for " + newsgroup);
            }
        }

        private void createNewsgroupList(SPWeb web)
        {
            try
            {
                Guid newNewsgroupListID = web.Lists.Add(newsgroup, NewsGroupDescription, SPListTemplateType.GenericList);
                web.Update();

                string newsgroupSimple = Regex.Replace(newsgroup, "[^A-Za-z0-9]", "");
                string pageUrl = web.Url + "/Lists/" + newsgroupSimple + "/AllItems.aspx";

                SPList newsgroupList = web.Lists[newsgroup];

                newsgroupList.EnableSyndication = true;
                newsgroupList.OnQuickLaunch = false;
                newsgroupList.ContentTypesEnabled = true;
                newsgroupList.EnableAttachments = false;
                newsgroupList.EnableFolderCreation = false;
                newsgroupList.EnableVersioning = false;

                SPContentType item = newsgroupList.ContentTypes["Item"];
                newsgroupList.ContentTypes.Delete(item.Id);

                SPContentType newsPost = web.ContentTypes["News Post"];
                newsgroupList.ContentTypes.Add(newsPost);

                newsgroupList.Fields[SPBuiltInFieldId.Title].Title = "Post Subject";
                newsgroupList.Fields[SPBuiltInFieldId.Title].Update();
                newsgroupList.Fields[SPBuiltInFieldId.LinkTitle].Title = "Post Subject";
                newsgroupList.Fields[SPBuiltInFieldId.LinkTitle].Update();
                newsgroupList.Fields[SPBuiltInFieldId.LinkTitleNoMenu].Title = "Post Subject";
                newsgroupList.Fields[SPBuiltInFieldId.LinkTitleNoMenu].Update();

                newsgroupList.Update();

                string webPartButtonXml;
                MakeWebPartXML(web, out webPartButtonXml);

                WebPartPagesWebService service = new WebPartPagesWebService();
                service.Url = web.Site.Url + "/_vti_bin/WebPartPages.asmx";
                service.PreAuthenticate = true;
                service.Credentials = System.Net.CredentialCache.DefaultCredentials;

                try
                {
                    Guid newPartId2 = service.AddWebPartToZone(web.Url + "/Lists/" + newsgroupSimple + "/AllItems.aspx", webPartButtonXml, NewsgroupListSync.WebPartReference.Storage.Shared, "Main", 1);
                }
                catch (Exception ex)
                {
                    //throw new Exception("Problem adding webpart: " + ex.StackTrace);
                }

                UpdateNewsgroupListView(newsgroupList);

                service.Dispose();
            }
            catch (Exception ex)
            {
                throw new SPException("I wasn't able to create the " + newsgroup + " list for you because " + ex.Message);
            }
        }

        private void MakeWebPartXML(SPWeb web, out string webPartButtonXml)
        {
            // add the post button to the top of the list via a new webpart
            string buttonHtml = "\n<style type=\"text/css\">\n";
            buttonHtml += "a.squarebutton{\n";
            buttonHtml += "background: transparent url('/tmw/images/square-blue-left.gif') no-repeat top left;\n";
            buttonHtml += "display: block;\n";
            buttonHtml += "float: right;\n";
            buttonHtml += "font: normal 12px Arial;\n";
            buttonHtml += "line-height: 15px;\n";
            buttonHtml += "height: 23px;\n";
            buttonHtml += "padding-left: 9px;\n";
            buttonHtml += "text-decoration: none;\n";
            buttonHtml += "}\n";
            buttonHtml += "a:link.squarebutton, a:visited.squarebutton, a:active.squarebutton{\n";
            buttonHtml += "color: #494949;\n";
            buttonHtml += "}\n";
            buttonHtml += "a.squarebutton span{\n";
            buttonHtml += "background: transparent url('/tmw/images/square-blue-right.gif') no-repeat top right;\n";
            buttonHtml += "display: block;\n";
            buttonHtml += "padding: 4px 9px 4px 0;\n";
            buttonHtml += "}\n";
            buttonHtml += "a.squarebutton:hover{\n";
            buttonHtml += "background-position: bottom left;\n";
            buttonHtml += "}\n";
            buttonHtml += "a.squarebutton:hover span{\n";
            buttonHtml += "background-position: bottom right;\n";
            buttonHtml += "text-decoration: none;\n";
            buttonHtml += "color: black;\n";
            buttonHtml += "}\n";
            buttonHtml += ".buttonwrapper{\n";
            buttonHtml += "overflow: hidden;\n";
            buttonHtml += "width: 50%;\n";
            buttonHtml += "}\n";
            buttonHtml += "div.ms-PartSpacingVertical{ display:none }\n";
            buttonHtml += "</style>\n";
            buttonHtml += "<div class=\"buttonwrapper\">\n";
            buttonHtml += "<a class=\"squarebutton\" href=\"#\"";
            buttonHtml += " onclick=\"javscript:document.location.href='" + web.Url + "/_layouts/newsgroups/NewMessage.aspx?NewsGroup=" + newsgroup + "';return false;\"><span>Post to " + newsgroup + "</span></a>\n";
            buttonHtml += "</div>\n";

            webPartButtonXml = "<?xml version=\"1.0\" encoding=\"utf-16\"?>\n" +
            "<WebPart xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://schemas.microsoft.com/WebPart/v2\">\n" +
            "<Title>Post to " + newsgroup + "</Title>\n  " +
            "<FrameType>None</FrameType>\n  " +
            "<Description>Use for formatted text, tables, and images.</Description>\n  " +
            "<IsIncluded>true</IsIncluded>\n  " +
            "<ZoneID>Main</ZoneID>\n  " +
            "<PartOrder>1</PartOrder>\n  " +
            "<FrameState>Normal</FrameState>\n  " +
            "<Height />\n  " +
            "<Width />\n  " +
            "<AllowRemove>true</AllowRemove>\n  " +
            "<AllowZoneChange>true</AllowZoneChange>\n  " +
            "<AllowMinimize>true</AllowMinimize>\n  " +
            "<IsVisible>true</IsVisible>\n  " +
            "<DetailLink />\n  " +
            "<HelpLink />\n  " +
            "<Dir>Default</Dir>\n  " +
            "<PartImageSmall />\n  " +
            "<MissingAssembly />\n  " +
            "<PartImageLarge>/_layouts/images/mscontl.gif</PartImageLarge>\n  " +
            "<IsIncludedFilter />\n  " +
            "<Assembly>Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly>\n  " +
            "<TypeName>Microsoft.SharePoint.WebPartPages.ContentEditorWebPart</TypeName>\n  " +
            "<ContentLink xmlns=\"http://schemas.microsoft.com/WebPart/v2/ContentEditor\" />\n  " +
            "<Content xmlns=\"http://schemas.microsoft.com/WebPart/v2/ContentEditor\"><![CDATA[" + buttonHtml + "]]></Content>\n  " +
            "<PartStorage xmlns=\"http://schemas.microsoft.com/WebPart/v2/ContentEditor\" />\n</WebPart>";            
        }

        private void UpdateNewsgroupListView(SPList list)
        {
            SPView defaultView = list.DefaultView;
            defaultView.ViewFields.DeleteAll();
            defaultView.ViewFields.Add("LinkTitle");
            defaultView.ViewFields.Add("PostFrom");
            defaultView.ViewFields.Add("PostDate");
            defaultView.Query = "<OrderBy><FieldRef Name=\"Created\" Ascending=\"False\" /></OrderBy>";
            defaultView.Update();
        }

        private void addNewsItem(ArticleHeadersDictionary header, StringBuilder sb, SPWeb web)
        {
            try
            {
                SPListItemCollection listItems = web.Lists[newsgroup].Items;
                SPListItem item = listItems.Add();

                item["Post Subject"] = header.ContainsKey("Subject") ? header["Subject"][0] : "Undefined";
                int postID = header.ContainsKey("PostID") ? Convert.ToInt32(header["PostID"][0]) : 0;
                item["PostID"] = postID;
                string postFrom = header.ContainsKey("From") ? header["From"][0] : "Undefined";
                item["Post From"] = postFrom;
                try
                {
                    Regex emailregex = new Regex(@"([a-zA-Z_0-9.-]+\@[a-zA-Z_0-9.-]+\.\w+)", RegexOptions.IgnoreCase | RegexOptions.Compiled);
                    Match userEmail = emailregex.Match(postFrom);

                    string loginName = SPUtility.GetLoginNameFromEmail(web.Site, userEmail.ToString());
                    SPUser localUser = web.EnsureUser(loginName);

                    item["Created By"] = localUser.ID;
                    item["Modified By"] = localUser.ID;
                }
                catch { }

                item["Post Body"] = sb.ToString();

                try
                {
                    if (header.ContainsKey("Date"))
                    {
                        IFormatProvider format = new System.Globalization.CultureInfo("en-US", true);
                        string s = header["Date"][0].Replace("(UTC)", "");
                        DateTime sentOn = DateTime.Parse(s, format);
                        item["Post Date"] = sentOn;
                        item["Created"] = sentOn;
                        item["Modified"] = sentOn;
                    }
                }
                catch
                {
                    item["Post Date"] = DateTime.Now.ToLocalTime();
                }

                item["Post a reply"] = web.Url + "/_layouts/newsgroups/Reply.aspx?articleID=" + postID + "&NewsGroup=" + newsgroup + ", Reply to this post";
                item.Update();
            }
            catch
            {
                //throw new SPException("I wasn't able addnewsitem to " + newsgroup);
            }
        }

        private bool listExists(SPListCollection collection, string title)
        {
            foreach (SPList list in collection)
            {
                if (list.Title == title)
                {
                    return true;
                }
            }
            return false;
        }
    }
}